-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local log = require 'mc.logging'
local client = require 'bios.client'
local bios_factory = require 'factory.bios_factory'
local privilege_check = {}

local UserMgmt = 2 ^ 4
local NormalHeartBeat = 0
local SPPresent = 1
local PACKAGE_PATH<const> = '/bmc/kepler/Managers/1/Package'

local function get_table(tbl, key)
    if tbl[key] then
        return tbl[key]
    end
    tbl[key] = {}
    return tbl[key]
end

local function is_need_check()
    local ok, ret = pcall(function()
        local bios_ser = bios_factory.get_service('bios_service')
        if not bios_ser then
            log:error("[bios]bios service is nil")
            return true
        end

        local is_multihost = bios_ser:is_multihost()
        if is_multihost then
            log:notice('[bios]multihost no need check sp')
            return false
        end
        return true
    end)
    if ok and not ret then
        return false
    end
    return true
end

-- 1、挂载BMC板载的SP（BMC模拟U盘挂载在OS侧）：Soctrl下的Ums判断是否挂载SP
-- 2、挂载直接加载的镜像SP（用户使用镜像启动SP）：通过host_agent检测是否有SP心跳
local function check_sp_state()
    if not is_need_check() then
        return true
    end

    local con_state = false
    client:ForeachUmsObjects(function (ums_obj)
        if string.sub(ums_obj.path, -2) == 'SP' then
            con_state = ums_obj.ConnectState
        end
    end)
    -- 第一种情况
    if con_state == SPPresent then
        log:notice('[bios] sp is prensent')
        return true
    end
    local sp_heart_beat = {}
    client:ForeachSmsObjects(function (oms_obj)
        local tbl = get_table(sp_heart_beat, oms_obj.path)
        tbl.registered = oms_obj.Registered
        tbl.type = oms_obj.Type
    end)
    client:ForeachSmsStatusObjects(function (oms_state_obj)
        local tbl = get_table(sp_heart_beat, oms_state_obj.path)
        tbl.heart_beat_state = oms_state_obj.HeartBeatState
    end)
    for _, v in pairs(sp_heart_beat) do
        -- 第二种情况
        if v.registered and v.type == 'SmartProvisioning' and v.heart_beat_state == NormalHeartBeat then
            log:notice('[bios] sp heart beat is normal')
            return true
        end
    end

    return false
end

local function get_sp_state()
    local ok, res = pcall(function()
        return check_sp_state()
    end)
    if not ok then
        return false
    end
    return res
end

function get_customer()
    local package_objs = client:GetPackageObjects()[PACKAGE_PATH]
    if package_objs == nil then
        log:error('[bios]Get package objects failed')
        return ''
    end
    local customer = package_objs.Customer
    if customer == nil then
        log:error('[bios]Get customer failed')
        return ''
    end
    return customer
end

function privilege_check.check(ctx)
    local priv = tonumber(ctx.Privilege)
    if not priv then
        log:error('[bios]current user has no privilege')
        return false
    end
    local sp_state = get_sp_state()
    local host_channel = ctx and ctx.UserName and ctx.UserName == '<host sms>'
    -- SP在位并且该请求是SP发送的，则降权为操作员
    if sp_state and host_channel then
        log:notice('[bios]SP with inner token set bios setting, skip SecureMgmt')
        return true
    elseif (priv & UserMgmt) == 0 then
        log:error('[bios]current user has no privilege to set bios setting')
        return false
    end
    return true
end

return privilege_check